import 'package:dio/dio.dart';
import 'package:flutter_worker730/constants/index.dart';
import 'package:flutter_worker730/utils/EventBus.dart';
import 'package:flutter_worker730/utils/PromptAction.dart';
import 'package:flutter_worker730/utils/TokenManager.dart';

class RequestDio {
  //可以设置dio 的基础地址
  final Dio _dio = Dio();
  RequestDio() {
    // 针对dio的基础地址进行设置
    // 基础地址 超时时间
    // _dio.options.baseUrl = 'https://live-api.itheima.net';
    // _dio.options.connectTimeout = Duration(seconds: 5);
    // _dio.options.sendTimeout = Duration(seconds: 5);
    // dart中对 对象 连续赋值  可以使用..省略
    _dio.options
      ..baseUrl = GlobalConstants.BASE_URL
      ..connectTimeout = Duration(seconds: GlobalConstants.TIME_OUT)
      ..sendTimeout = Duration(seconds: GlobalConstants.TIME_OUT);

    //配置拦截器
    _setInterceptors();
  }

  // 请求刷新token
  Future<bool> _refreshToken() async {
    try {
      // 不能再用_dio请求了  里面存的是过期的token
      // return _dio.post(
      //   HTTP_PATH.USER_REFRESH_TOKEN,
      // );
      String refreshToken = tokenManager.getRefreshToken();

      Dio dio = Dio(); // 创建一个新的dio实例请求
      var res = await dio.post(
          GlobalConstants.BASE_URL + HTTP_PATH.USER_REFRESH_TOKEN,
          options: Options(headers: {'Authorization': 'Bearer $refreshToken'}));

      if (res.data['code'] == GlobalConstants.CODE) {
        // 刷新token成功!!!!
        tokenManager.setToken(res.data['data']['token'],
            refreshToken: res.data['data']['refreshToken']);
        return true;
      } else {
        // 刷新token失败!!!!
        return false;
      }
      // 后端返回的数据
    } catch (e) {
      // 刷新token失败!!!!
      return false;
    }
  }

  // 配置拦截器
  _setInterceptors() {
    _dio.interceptors.add(InterceptorsWrapper(
        // 添加一个请求拦截器  options请求配置对象 {}   handler处理函数对象
        // 触发时机:  请求到服务器之前 先走这个函数逻辑 然后再发出  =>  必须放行
        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
      // 在请求被发送之前做一些事情
      // options.headers['Authorization'] = 'Bearer token';
      String token = tokenManager.getToken(); // ''
      if (token != '') {
        // 添加请求头
        options.headers['Authorization'] = 'Bearer $token';
        // options.headers['a'] = 'b';
      }
      handler.next(options); //continue
    },
        // 添加一个响应拦截器  response响应数据对象 {}   handler处理函数对象   =>  必须放行
        // 触发时机:  服务器返回数据到客户端之前 先走这个函数逻辑 然后再返回给客户端
        onResponse:
            (Response<dynamic> response, ResponseInterceptorHandler handler) {
      // 在返回响应数据之前做一些事情
      // handler.next(response);
      //根据情况判断
      //目前只处理2xx ,其他统一抛出异常
      if (response.statusCode! >= 200 && response.statusCode! < 300) {
        //继续往下走
        handler.next(response);
      } else {
        // 4xx   401token问题  response.statusCode! == 401  清除token  回到登录 提示
        // 如果遇到2xx之外的状态码 应该处理一个失败的Future回去 有一个原因response.requestOptions
        // response.requestOptions 获取请求信息
        PromptAction.error(response.statusMessage ?? '网络错误');
        handler.reject(DioException(requestOptions: response.requestOptions));
      }
    },
        // 添加一个错误拦截器  exception异常对象 {}   handler处理函数对象   =>  必须放行
        onError:
            (DioException exception, ErrorInterceptorHandler handler) async {
      //判断exception.reponse?.statusCode 是否是 401 如果是401 利用tokenManager清空token
      if (exception.response?.statusCode == 401) {
        // 清空token
        // tokenManager.removeToken();
        // // 跳转到登录页面  无法跳转 一会处理
        // // Navigator.pu
        // //提示重新登录
        // eventBus.fire(LogoutEvent()); // login   logout
        // PromptAction.error('token过期请重新登录');

        // 一旦发现401 先看看refreshToken
        var refreshToken = tokenManager.getRefreshToken();
        if (refreshToken != '') {
          // 有refreshToken => 请求后端刷新token(封装函数)
          bool isOk = await _refreshToken(); // 刷新token的成功与否

          if (isOk) {
            // 将重新发送的请求 的成功结果  包装成一个成功的promise  返回给页面
            var newRes = await _dio.fetch(exception.requestOptions);
            return handler.resolve(newRes); // Promise.resolve()
            // 刷新失败
            // 无refreshToken
            // 清空token
            // tokenManager.removeToken();
            // // 跳转到登录页面  无法跳转 一会处理 => emitter  发布订阅模式  eventhub   eventbus
            // eventBus.fire(LogoutEvent()); // login   logout
            // // Navigator.pu
            // // 提示重新登录
            // PromptAction.error('token过期请重新登录');
          }
        } else {
          // 无refreshToken
          // 清空token
          tokenManager.removeToken();
          // flutter 的三方库有一个发布订阅叫eventbus
          // 下载 使用
          // 跳转到登录页面  无法跳转 一会处理 => emitter  发布订阅模式  eventhub   eventbus
          eventBus.fire(LogoutEvent()); // login   logout
          // Navigator.pu
          // 提示重新登录
          PromptAction.error('token过期请重新登录');
        }
        // 当请求失败时做一些事情
        handler.next(exception);
      }
    }));
  }

  // 封装一个get 替代他的get
  // Future get(String path, {Map<String, dynamic>? params}) async {
  Future get(String path,
      {Map<String, dynamic>? params, Map<String, dynamic>? data}) async {
    var res = await _dio.get(path, queryParameters: params, data: data);
    return _handleResponse(res);
  }

// 封装一个 post 替代他的post
  Future post(String path,
      {Map<String, dynamic>? data, Map<String, dynamic>? params}) async {
    var res = await _dio.post(path, data: data, queryParameters: params);
    return _handleResponse(res);
  }

  // 封装一个 post 替代他的post
  Future upload(String path,
      {FormData? data, Map<String, dynamic>? params}) async {
    var res = await _dio.post(path, data: data, queryParameters: params);
    return _handleResponse(res);
  }

  // 封装一个 put 替代他的put
  Future put(String path,
      {Map<String, dynamic>? data, Map<String, dynamic>? params}) async {
    var res = await _dio.put(path, data: data, queryParameters: params);
    return _handleResponse(res);
  }

  // 封装一个 delete 替代他的delete
  Future delete(String path,
      {Map<String, dynamic>? params, Map<String, dynamic>? data}) async {
    var res = await _dio.delete(path, queryParameters: params, data: data);
    return _handleResponse(res);
  }

  // 封装一个处理响应结果的函数  res.data['data']
  _handleResponse(Response<dynamic> res) {
    if (res.data['code'] == GlobalConstants.CODE) {
      // 10000 才有数据  剥离数据
      return res.data['data'];
    }
    // 提示
    PromptAction.error(res.data['message']);
    // 提示个消息 使用第三方插件的toast弹出
    throw Exception(res.data['message']);
  }
}

final RequestDio requestDio = RequestDio();
